home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / insserv / check-initd-order next >
Text File  |  2009-09-07  |  13KB  |  416 lines

  1. #!/usr/bin/perl
  2. #
  3. # Author: Petter Reinholdtsen
  4. # Date:   2005-08-21
  5. #
  6. # Read LSM init.d headers in SysV init.d scripts, and verify correct
  7. # start order for all runlevels.  It can also provide a graph.
  8. #
  9. # To generate a graph, run it like this
  10. #
  11. #   check-initd-order -g > initorder.dotty && dotty initorder.dotty
  12.  
  13. use strict;
  14. use warnings;
  15.  
  16. my $rcbase = "/etc";
  17. #$rcbase = "/opt/ltsp/i386/etc";
  18.  
  19. my $overridepath = "/usr/share/insserv/overrides";
  20. my $hostoverridepath =  "/etc/insserv/overrides";
  21.  
  22. my $debug = 0;
  23. my $errors = 0;
  24.  
  25. my %rcmap =
  26.     (
  27.      'B' => 'rc.boot',
  28.      'S' => 'rcS.d',
  29.      '1' => 'rc1.d',
  30.      '2' => 'rc2.d',
  31.      '3' => 'rc3.d',
  32.      '4' => 'rc4.d',
  33.      '5' => 'rc5.d',
  34.      '6' => 'rc6.d',
  35.      );
  36.  
  37. my %sysmap;
  38.  
  39. my %provideslist;
  40. my %scriptorder;
  41. my %opts;
  42.  
  43. # Used to draw graphs
  44. my %gotrevdeps;
  45. my %allprovides;
  46.  
  47. load_sysmap("/etc/insserv.conf");
  48.  
  49. while($#ARGV >= 0 && ($_ = $ARGV[0]) =~ /^-/) {
  50.         shift @ARGV;
  51.         if (/^-([cdgko])$/) { $opts{$1}++; next }
  52.         if (/^-h|--help$/) { &usage; }
  53.         &usage("unknown option");
  54. }
  55.  
  56. $debug = $opts{'d'};
  57. my $useoverrides = $opts{'o'} ? 0 : 1;
  58.  
  59. if ($opts{'g'}) {
  60.     graph_generate();
  61.     exit 0;
  62. }
  63.  
  64. check_bootorder();
  65. exit $errors > 0 ? 1 : 0;
  66.  
  67. sub usage {
  68.     print STDERR "check-initd-order: error: @_\n" if ($#_ >= 0);
  69.     print STDERR <<EOF;
  70. usage: check-initd-order [-cdgko]
  71.   -d enable debug output
  72.   -o do not load override files
  73.   -k use shutdown (reboot) sequence instead of boot sequence
  74.   -g generate graph
  75.   -c use combined boot and shutdown sequence (only for graphs)
  76. EOF
  77.     exit 1;
  78. }
  79.  
  80. # Simple basename implementatin to avoid dependin on File::Basename
  81. # from perl-modules
  82. sub basename {
  83.     my $path = shift;
  84.     $path =~ s%^.*/([^/]+)$%$1%;
  85.     return $path;
  86. }
  87.  
  88. sub error {
  89.     print STDERR "error: ", @_;
  90.     $errors++;
  91. }
  92.  
  93. # Map packages to system metapackages.  These dependencies should
  94. # probably be more complex
  95. sub load_sysmap {
  96.     my $filename = shift;
  97.     open(CONF, "<", "$filename") || die "Unable to load $filename";
  98.     while (<CONF>) {
  99.         chomp;
  100.         s/\#.*$//;
  101.         next if m/^\s*$/;
  102.         if (m/^(\$\S+)\s+(\S.*\S*)\S*$/) {
  103.             my $virt = $1;
  104.             for my $dep (split(/\s+/, $2)) {
  105.                 $dep =~ s/^\+//g;
  106.                 $sysmap{$dep} = $virt;
  107.             }
  108.         }
  109.     }
  110.     close(CONF);
  111. }
  112.  
  113. sub graph_addnode {
  114.     my ($isstopseq, $lsbinforef) = @_;
  115.     my %lsbinfo = %{$lsbinforef};
  116.  
  117.     unless ($lsbinfo{'provides'}) {
  118.         error "File ". $lsbinfo{'file'} . " is missing the provides header\n";
  119.         $lsbinfo{'provides'} = $lsbinfo{'file'};
  120.         $lsbinfo{'provides'} =~ s/^[SK]\d{2}//;
  121.     }
  122.  
  123.     my $key = $opts{'k'} ? 'stop' : 'start';
  124.     my $revkey = $opts{'k'} ? 'stop-after' : 'start-before';
  125.     my @provides = split(/\s+/, $lsbinfo{'provides'});
  126.     for my $name (@provides) {
  127.         if (exists $sysmap{$name}) {
  128.             graph_addnode($isstopseq,
  129.                           {'provides'      => $sysmap{$name},
  130.                           "required-$key" => $name});
  131.         }
  132.     }
  133.  
  134.     if (1 < @provides) {
  135.         my @providescopy = @provides;
  136.         my $lastprovide = shift @providescopy;
  137.         for my $provide (@providescopy) {
  138.             graph_addnode($isstopseq,
  139.                           {'provides'      => $lastprovide,
  140.                            "required-$key" => $provide});
  141.             graph_addnode($isstopseq,
  142.                           {'provides'      => $provide,
  143.                            "required-$key" => $lastprovide});
  144.         }
  145.     }
  146.  
  147.     for my $provide (@provides) {
  148.         my %deps =
  149.             (
  150.              "required-$key" => 'blue',
  151.              "should-$key" => 'springgreen',
  152.              "$revkey" => 'yellow'
  153.              );
  154.  
  155.         for $key (keys %deps) {
  156.             if (exists $lsbinfo{$key} && $lsbinfo{$key}) {
  157.                 my @depends = split(/\s+/, $lsbinfo{$key});
  158.  
  159.                 my $dependonall = 0;
  160.                 for my $pkg (@depends) {
  161.                     $dependonall = 1 if ($pkg eq '$all');
  162.                 }
  163.  
  164.                 for my $pkg (@depends) {
  165.                     my $color = $deps{$key};
  166.                     if ($revkey eq $key) {
  167.                         print "\"$provide\" -> \"$pkg\"[color=$color] ;\n";
  168.                         $gotrevdeps{$pkg} = 1 unless $dependonall;
  169.                     } else {
  170.                         print "\"$pkg\" -> \"$provide\"[color=$color] ;\n";
  171.                         $gotrevdeps{$provide} = 1 unless $dependonall;
  172.                     }
  173.                 }
  174.             }
  175.         }
  176.  
  177.         print "\"$provide\" [shape=box];\n" unless $allprovides{$provide};
  178.         $allprovides{$provide} = 1;
  179.     }
  180. }
  181.  
  182. sub graph_generate_mode {
  183.     my ($isstopseq) = @_;
  184.     my @dirs = $isstopseq ? $rcmap{6} : ($rcmap{S}, $rcmap{2});
  185.     for my $rcdir (@dirs) {
  186.         chdir "$rcbase/$rcdir/.";
  187.         my @scripts = $isstopseq ? <K*> : <S*>;
  188.         for my $script (@scripts) {
  189.             my $lsbinforef = load_lsb_tags("$rcbase/$rcdir/$script",
  190.                                            $useoverrides);
  191.  
  192.             unless (defined $lsbinforef) {
  193.                 error "LSB header missing in $rcbase/$rcdir/$script\n";
  194.                 $script =~ s/^[SK]\d{2}//;
  195.                 $lsbinforef = {'provides'       => $script,
  196.                                'required-start' => '$remote_fs $syslog',
  197.                                'required-stop'  => '$remote_fs $syslog'};
  198.             }
  199.             graph_addnode($isstopseq, $lsbinforef);
  200.         }
  201.     }
  202.     # Mark all packages without any reverse dependencies as depending
  203.     # on $all
  204.     for my $provide (keys %allprovides) {
  205.         next unless (exists $gotrevdeps{$provide});
  206.         my $lsbinforef = {'provides'       => '$all',
  207.                           'required-start' => "$provide",
  208.                           'required-stop'  => "$provide"};
  209.         graph_addnode($isstopseq, $lsbinforef);
  210.     }
  211. }
  212.  
  213. sub graph_generate {
  214.     print "# Generating graph\n";
  215.     print <<EOF;
  216. digraph packages {
  217. rankdir=LR;
  218. concentrate=true;
  219. EOF
  220.     if ($opts{'c'}) {
  221.         graph_generate_mode();
  222.         graph_generate_mode(1);
  223.     } else {
  224.         graph_generate_mode($opts{'k'});
  225.     }
  226.     print <<EOF;
  227. }
  228. EOF
  229. }
  230.  
  231. sub check_deps {
  232.     my ($lsbinforef, $tag, $order, $bootorder, $headername, $required) = @_;
  233.     my %lsbinfo = %{$lsbinforef};
  234.     my $name = $lsbinfo{'file'};
  235.     if ($lsbinfo{$headername}) {
  236.         my @depends = split(/\s+/, $lsbinfo{$headername});
  237.         for my $dep (@depends) {
  238.             if (! $required && exists $provideslist{$dep}) {
  239.                 unless (exists $scriptorder{$tag}{$dep}
  240.                         and ("S" eq $tag
  241.                              ? $scriptorder{$tag}{$dep} < $bootorder
  242.                              : $scriptorder{$tag}{$dep} > $bootorder)) {
  243.                     my $deporder;
  244.                     if (exists $scriptorder{$tag}{$dep}) {
  245.                         $deporder = $scriptorder{$tag}{$dep}
  246.                     } else {
  247.                         $deporder = exists $provideslist{$dep} ? $provideslist{$dep} : "?";
  248.                     }
  249.                     error(sprintf("Incorrect order %s@%s %s %s%s\n",
  250.                            $dep, $deporder, 'S' eq $tag ? '>' : '<',
  251.                            $name, $order));
  252.                 }
  253.             }
  254.         }
  255.     }
  256. }
  257.  
  258. sub check_bootorder {
  259.     my $bootorder = 0;
  260.     my @dirs = $opts{'k'} ? $rcmap{6} : ($rcmap{S}, $rcmap{2});
  261.     my @scripts;
  262.     for my $rcdir (@dirs) {
  263. #        chdir "$rcbase/$rcdir/.";
  264.         push(@scripts, $opts{'k'} ? <$rcbase/$rcdir/K*> : <$rcbase/$rcdir/S*>);
  265.     }
  266.  
  267.     if ($opts{'k'}) {
  268.         $scriptorder{'K'}{'$all'} = 1;
  269.     } else {
  270.         # Calculate script order for the script before the scripts
  271.         # with the last boot sequence number.
  272.         my $tmpbootorder = 0;
  273.         my $allorder = 0;
  274.         my $maxorder = 0;
  275.         my $maxbootorder = 0;
  276.         for my $scriptpath (@scripts) {
  277.             my $script = $scriptpath;
  278.             $script =~ s%^.*/([^/]+)$%$1%;
  279.             $tmpbootorder++;
  280.             my ($tag, $order, $name) = $script =~ m/^(.)(\d{2})(.+)$/;
  281.             if ($order > $maxorder) {
  282.                 $allorder = $maxbootorder;
  283.                 $maxbootorder = $tmpbootorder;
  284.                 $maxorder = $order;
  285.             }
  286.  
  287.             my $lsbinforef = load_lsb_tags($scriptpath,
  288.                                            $useoverrides);
  289.  
  290.             if (exists $lsbinforef->{'provides'}) {
  291.                 for my $provide (split(/\s+/, $lsbinforef->{'provides'})) {
  292.                     $provideslist{$provide} = $order;
  293.                 }
  294.             } else {
  295.                 $provideslist{$script} = $order;
  296.             }
  297.         }
  298.         $scriptorder{'S'}{'$all'} = $allorder;
  299.     }
  300.     for my $scriptpath (@scripts) {
  301.         my $script = $scriptpath;
  302.         $script =~ s%^.*/([^/]+)$%$1%;
  303.         $bootorder++;
  304.         my ($tag, $order, $name) = $script =~ m/^(.)(\d{2})(.+)$/;
  305.  
  306.         $scriptorder{$tag}{$name} = $bootorder;
  307.         $scriptorder{$tag}{$sysmap{$name}} = $bootorder
  308.             if (exists $sysmap{$name});
  309.  
  310. #           print "$script\n";
  311. #           print "T: $tag O: $order N: $name\n";
  312.         my $lsbinforef = load_lsb_tags($scriptpath,
  313.                                        $useoverrides);
  314.  
  315.         unless (defined $lsbinforef) {
  316.             error "LSB header missing in $scriptpath\n";
  317.             next;
  318.         }
  319.         my %lsbinfo = %{$lsbinforef};
  320.  
  321.         for my $provide (split(/\s+/, $lsbinfo{'provides'})) {
  322.             $scriptorder{$tag}{$provide} = $bootorder;
  323.             $scriptorder{$tag}{$sysmap{$provide}} = $bootorder
  324.                 if (exists $sysmap{$provide});
  325.         }
  326.  
  327.         if ('S' eq $tag) {
  328.             check_deps($lsbinforef, $tag, $order, $bootorder, 'required-start', 1);
  329.             check_deps($lsbinforef, $tag, $order, $bootorder, 'should-start', 0);
  330. #            check_deps($lsbinforef, 'K', $order, $bootorder, 'start-before', 0);
  331.         }
  332.         if ('K' eq $tag) {
  333.             check_deps($lsbinforef, $tag, $order, $bootorder, 'required-stop', 1);
  334.             check_deps($lsbinforef, $tag, $order, $bootorder, 'should-stop', 0);
  335. #            check_deps($lsbinforef, 'S', $order, $bootorder, 'stop-after', 0);
  336.         }
  337.     }
  338. }
  339.  
  340. sub load_lsb_tags {
  341.     my ($initfile, $useoverrides) = @_;
  342.     my $lsbinforef = load_lsb_tags_from_file($initfile);
  343.  
  344.     if ($useoverrides) {
  345.         # Try override file
  346.         $initfile = readlink($initfile) if (-l $initfile);
  347.         my $basename = basename($initfile);
  348.  
  349.         # Only read shipped override file when initscript does not
  350.         # contain LSB tags.
  351.         if (! defined($lsbinforef) && -f "$overridepath/$basename") {
  352.             print STDERR "Override $overridepath/$basename\n" if $debug;
  353.             $lsbinforef = load_lsb_tags_from_file("$overridepath/$basename");
  354.         }
  355.  
  356.         # Always read the host override in $hostoverridepath.
  357.         if (-f "$hostoverridepath/$basename") {
  358.             print STDERR "Override $hostoverridepath/$basename\n" if $debug;
  359.             $lsbinforef = load_lsb_tags_from_file("$hostoverridepath/$basename");
  360.         }
  361.  
  362.     }
  363.     return $lsbinforef;
  364. }
  365.  
  366. sub load_lsb_tags_from_file {
  367.     my ($file) = @_;
  368.     print STDERR "Loading $file\n" if $debug;
  369.     ### BEGIN INIT INFO
  370.     # Provides:          xdebconfigurator
  371.     # Required-Start:    $syslog
  372.     # Required-Stop:     $syslog
  373.     # Default-Start:     2 3 4 5
  374.     # Default-Stop:      1 6
  375.     # Short-Description: Genererate xfree86 configuration at boot time
  376.     # Description:       Preseed X configuration and use dexconf to
  377.     #                    genereate a new configuration file.
  378.     ### END INIT INFO
  379.     unless (open(FILE, "<$file")) {
  380.         warn "error: Unable to read $file";
  381.         return;
  382.     }
  383.     my $found = 0;
  384.     my ($provides, $requiredstart, $requiredstop, $shouldstart, $shouldstop);
  385.     my ($startbefore, $stopafter);
  386.     while (<FILE>) {
  387.         chomp;
  388.         $found = 1 if (m/\#\#\# BEGIN INIT INFO/);
  389.         next unless $found;
  390.         last if (m/\#\#\# END INIT INFO/);
  391.  
  392.         $provides = $1      if (m/^\# provides:\s+(\S*.*\S+)\s*$/i);
  393.         $requiredstart = $1 if (m/^\# required-start:\s+(\S*.*\S+)\s*$/i);
  394.         $requiredstop = $1  if (m/^\# required-stop:\s+(\S*.*\S+)\s*$/i);
  395.         $shouldstart = $1   if (m/^\# should-start:\s+(\S*.*\S+)\s*$/i);
  396.         $shouldstop = $1    if (m/^\# should-stop:\s+(\S*.*\S+)\s*$/i);
  397.         $startbefore = $1   if (m/^\# X-Start-Before:\s+(\S*.*\S+)\s*$/i);
  398.         $stopafter = $1     if (m/^\# X-Stop-After:\s+(\S*.*\S+)\s*$/i);
  399.     }
  400.     close(FILE);
  401.  
  402.     return undef unless ($found);
  403.  
  404. #    print "Provides: $provides\n" if $provides;
  405.     return {
  406.             'provides'       => $provides,
  407.             'required-start' => $requiredstart,
  408.             'required-stop'  => $requiredstop,
  409.             'should-start'   => $shouldstart,
  410.             'should-stop'    => $shouldstop,
  411.             'start-before'   => $startbefore,
  412.             'stop-after'     => $stopafter,
  413.             'file'           => $file,
  414.             };
  415. }
  416.